package com.op.parcel.manage.config.security.jwt;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.op.parcel.common.api.R;
import com.op.parcel.common.constant.SecurityConstant;
import com.op.parcel.common.enums.ResultEnum;
import com.op.parcel.manage.config.security.util.HttpDataUtil;
import com.op.parcel.manage.handler.RedisUser;
import io.micrometer.core.instrument.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author xuan🐽
 */
@Slf4j
@Component
public class JWTAuthenticationFilter extends OncePerRequestFilter {

	@Value("${user.loginTime}")
	private Integer loginTime;

	@Autowired
	private ObjectMapper objectMapper;

	@Autowired
	private RedisTemplate<String, String> redisTemplate;

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
		String header = request.getHeader(SecurityConstant.HEADER);
		if (StringUtils.isBlank(header)) {
			header = request.getParameter(SecurityConstant.HEADER);
		}
		PathMatcher pathMatcher = new AntPathMatcher();
		if (pathMatcher.match("**/login", request.getRequestURL().toString())) {
			chain.doFilter(request, response);
		}
		if (StringUtils.isNotEmpty(header)) {
			try {
				getAuthentication(header, response);
				chain.doFilter(request, response);
			} catch (IOException e) {
				log.error("jwt:{}", e);
				HttpDataUtil.respJson(response, R.error("请求失败，请稍后再试"));
			}
		} else {
			chain.doFilter(request, response);
			//TODO xuan swagger..被拦截
//			HttpDataUtil.respJson(response, R.error(ResultEnum.UNLOGIN.getCode(), "未登陆"));
		}
	}

	public void getAuthentication(String header, HttpServletResponse response) throws IOException {
		String tokenRedis = redisTemplate.opsForValue().get(SecurityConstant.TOKEN_PRE + header);
		if (StringUtils.isBlank(tokenRedis)) {
			HttpDataUtil.respJson(response, R.error(ResultEnum.UNLOGIN.getCode(), "登录已失效，请重新登录"));
			return;
		}
		RedisUser redisUser = objectMapper.readValue(tokenRedis, RedisUser.class);
		Collection<GrantedAuthority> authorities = redisUser.getPermissions().stream()
			.map(SimpleGrantedAuthority::new)
			.collect(Collectors.toList());
		String username = redisUser.getUsername();

		redisTemplate.opsForValue().set(SecurityConstant.TOKEN_PRE + header, objectMapper.writeValueAsString(redisUser), loginTime, TimeUnit.DAYS);
		redisTemplate.opsForValue().set(SecurityConstant.USER_TOKEN + redisUser.getUsername(), header, loginTime, TimeUnit.DAYS);

		if (StringUtils.isNotBlank(username)) {
			User principal = new User(username, "", authorities);
			UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(principal, null, authorities);
			SecurityContextHolder.getContext().setAuthentication(authenticationToken);
		}
	}
}

